package md.sv.dbu.executor;

import md.sv.dbu.DBUExecutor;
import md.sv.dbu.config.jaxb.SelectSQLType;
import md.sv.dbu.config.jaxb.UpdateBlockType;
import md.sv.dbu.config.jaxb.UpdateTaskType;
import md.sv.dbu.db.DBExecutor;
import md.sv.dbu.utils.DbuUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;

import java.util.List;

/**
 * @author MG
 */
public class UpdateTaskExecutor implements DbuTypeExecutor {
    private static final Logger logger = LoggerFactory.getLogger(UpdateTaskExecutor.class);

    private DBUExecutor dbuExecutor;

    @Override
    public void registerMainExecutor(DBUExecutor executor) {
        this.dbuExecutor = executor;
    }

    @Override
    public boolean isEligible(Object dbuElement) {
        return dbuElement instanceof UpdateTaskType;
    }

    @Override
    public void execute(Object dbuElement) {
        UpdateTaskType updateTask = (UpdateTaskType) dbuElement;
        executeUpdateTask(updateTask);
    }

    private void executeUpdateTask(UpdateTaskType updateTask) {
        if (!updateTask.isExecute()) {
            return;
        }
        logger.info("Executing update task: {}", updateTask.getDescription());

        DBExecutor mainExecutor = null;
        List<MapSqlParameterSource> namedParametersList;
        if (updateTask.getDatabase() != null) {
            mainExecutor = dbuExecutor.getDbExecutors().get(updateTask.getDatabase());
        }

        // execute the defined update blocks for each select sql
        for (SelectSQLType selectSQL : updateTask.getSelectSQL()) {
            DBExecutor executor = dbuExecutor.getExecutor(mainExecutor, selectSQL.getDatabase());

            if (executor != null) {
                logger.debug("Executing select query for the update task {}", updateTask.getDescription());
                namedParametersList = executor.selectQuery(selectSQL.getValue(), new MapSqlParameterSource(dbuExecutor.getVariables()), dbuExecutor.getVariables());
            } else {
                logger.error("Executor not defined for Update Task [{}] and for the UpdateTask select query", updateTask.getDescription());
                return;
            }

            if (namedParametersList.size() == 0) {
                logger.info("Select query returned no rows. Exiting update task [{}]", updateTask.getDescription());
                return;
            }

            if (updateTask.isBatch()) {
                for (UpdateBlockType updateBlock : updateTask.getUpdateBlock()) {
                    executor = dbuExecutor.getExecutor(mainExecutor, updateBlock.getDatabase());
                    if (executor != null) {
                        logger.debug("Executing in batch the update {}", updateBlock.getName());
                        DBExecutor.executeBatchUpdateBlock(dbuExecutor, updateBlock, executor, namedParametersList);
                    } else {
                        logger.error("Executor not defined for Update Task [{}], for the update {}", updateTask.getDescription(), updateBlock.getName());
                    }
                }
            } else {
                logger.debug("Executing individually the updates for task [{}]", updateTask.getDescription());
                for (MapSqlParameterSource parameterSource : namedParametersList) {
                    for (UpdateBlockType updateBlock : updateTask.getUpdateBlock()) {
                        executor = dbuExecutor.getExecutor(mainExecutor, updateBlock.getDatabase());
                        if (executor != null) {
                            DBExecutor.executeUpdateBlock(dbuExecutor, updateBlock, executor, parameterSource);
                        } else {
                            logger.error("Executor not defined for Update Task [{}], for the update {}", updateTask.getDescription(), updateBlock.getName());
                        }
                    }
                }
            }
            DbuUtils.UpdateVariables(dbuExecutor.getVariables(), namedParametersList.get(namedParametersList.size() - 1));
        }

        // if no select sql defined => a simple update task, to be executed only once
        if (updateTask.getSelectSQL().size() == 0) {
            for (UpdateBlockType updateBlock : updateTask.getUpdateBlock()) {
                logger.debug("Executing db [{}], for the update task [{}]", updateBlock.getName(), updateTask.getDescription());
                DBExecutor executor = dbuExecutor.getExecutor(mainExecutor, updateBlock.getDatabase());
                if (executor != null) {
                    DBExecutor.executeUpdateBlock(dbuExecutor, updateBlock, executor, new MapSqlParameterSource(dbuExecutor.getVariables()));
                } else {
                    logger.error("Executor not defined for Update Task [{}], for the execute {}", updateTask.getDescription(), updateBlock.getName());
                }
            }
        }

        dbuExecutor.commitExecutors();
    }

}
